package main

import (
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/spf13/cobra"

	"gitee.com/shuohe/go-shlib"
	"gitee.com/shuohe/go-shlib/xterm"

	"gitee.com/shuohe/simplecert"
)

var baseTimestamp = time.Date(2019, time.June, 8, 0, 0, 0, 0, time.UTC).UnixNano()

/**
 * 返回一个全局唯一的递增时间戳，用于给证书序列号赋值。
 */
func timestamp() int64 {
	return time.Now().UnixNano() - baseTimestamp
}

//=============================================================================

var defaultCa = &simplecert.CaInfo{
	Name:       "SimpleCert CA",
	KeySuite:   "2019.01",
	PrivateKey: shlib.LoadKeyFromBase58Ne("hjKXVyBcYFVmFhW6WHXnSMUVS6VPGvaPUuMHTkW4pxRP4rJHQGptU2fa7z7Z6x46AT4g2mCaN4PRGpzo5H39BqBf7tEnfraD5a4dDTB").(shlib.IPrivateKey),
	PublicKey:  shlib.LoadKeyFromBase58Ne("3N7H1WCwg26qzuLP3yyS7f3RVWZHhStMjt6kk6AABanRHQaTFtVbdQq4N949").(shlib.IPublicKey),
}

/**
 * 创建一个证书对象，签好名。
 */
func newSimpleCert(identityName string, usage string, pubKey shlib.IPublicKey) *simplecert.SimpleCert {
	cert := &simplecert.SimpleCert{
		Version:      "v1",
		SerialNumber: timestamp(),
		IdentityName: identityName,
		Usage:        usage,
		PublicKey:    pubKey,
		SignDate:     time.Now(),
		ExpireDate:   time.Now().AddDate(5, 0, 0),
	}
	cert.Sign(defaultCa)

	return cert
}

/**
 * 为指定实体创建公私钥对，将公钥制作成证书后保存，同时将私钥也写入文件保存。
 */
func createSimpleCert(identityName string, usage string, keyPasswd string, fileName string) error {
	key, _ := shlib.GenerateKey("ED25519")
	cert := newSimpleCert(identityName, usage, key.(shlib.IPrivateKey).Public())

	if err := cert.SaveToFile(fileName); err != nil {
		return errors.New("Can not create certificate file.")
	}
	if err := simplecert.WriteKeyFile(fileName+".key", key, keyPasswd); err != nil {
		return errors.New("Can not create key file.")
	}

	return nil
}

//=============================================================================
var optCert string

func doCreate(cmd *cobra.Command, args []string) {
	identityName := xterm.GetInput("Identity Name     : ")
	usage := xterm.GetInput("Usage             : ")
	keyFilePassword := xterm.GetPassword("Key File Password : ")

	if err := createSimpleCert(identityName, usage, keyFilePassword, optCert); err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Certificate file created with following CA configuration:")
	fmt.Println("    CA Name       :", defaultCa.Name)
	fmt.Println("    CA Key Suite  :", defaultCa.KeySuite)
	fmt.Println("    Ca Public Key :", shlib.Base58Encode(defaultCa.PublicKey.Export()))
	fmt.Printf("NOTE: The identity private key is stored in '%s'.\n", optCert+".key")
}

func doVerify(cmd *cobra.Command, args []string) {
	cert, err := simplecert.LoadSimpleCert(optCert)
	if err != nil {
		fmt.Printf("Error: Can not load certificate file.\n\t%v\n", err)
		return
	}

	// TODO: 检查 CaName, CaKeySuite 是否合法，并据此找到合适的验签公钥

	cert.Dump(os.Stdout)
	fmt.Println()

	if !cert.Verify(defaultCa) {
		fmt.Println("The certificate file is INVALID.")
	} else {
		fmt.Println("The certificate file is VALID.")
	}
}

func main() {
	createCmd := &cobra.Command{
		Use:   "create",
		Short: "Create a digital certificate file",
		Args:  cobra.NoArgs,
		Run:   doCreate,
	}
	createCmd.Flags().StringVarP(&optCert, "cert", "c", "", "Certificate file name")
	createCmd.MarkFlagRequired("cert")

	verifyCmd := &cobra.Command{
		Use:   "verify",
		Short: "Verify a digital certificate file",
		Args:  cobra.NoArgs,
		Run:   doVerify,
	}
	verifyCmd.Flags().StringVarP(&optCert, "cert", "c", "", "Certificate file name")
	verifyCmd.MarkFlagRequired("cert")

	rootCmd := &cobra.Command{
		Use:   "simplecert",
		Short: "A simple digital certificate tool.",
	}
	rootCmd.AddCommand(createCmd)
	rootCmd.AddCommand(verifyCmd)

	rootCmd.Execute()
}
